Skip to content

security: strip ANSI/OSC escapes from API-controlled output#479

Open
jeremy wants to merge 1 commit into
security/01-api-token-leakfrom
security/02-output-ansi-sanitize
Open

security: strip ANSI/OSC escapes from API-controlled output#479
jeremy wants to merge 1 commit into
security/01-api-token-leakfrom
security/02-output-ansi-sanitize

Conversation

@jeremy

@jeremy jeremy commented May 30, 2026

Copy link
Copy Markdown
Member

Strip ANSI/OSC escapes from API-controlled output

(Threat: T4 terminal injection via API-controlled strings carrying ANSI/OSC sequences.)

API-controlled strings (project/person/entity names, summaries, notices) reached lipgloss.Render/stdout and the --watch TUI without ansi.Strip, in render paths the generic table/cell path already protected.

Fix

  • Source: ansi.Strip in WithSummary/WithNotice/WithDiagnostic — protects all current and future callers.
  • Sinks (defense-in-depth): strip where Summary/Notice are emitted in the styled and markdown renderers (render.go, envelope.go).
  • --watch TUI: strip creator/action/title/excerpt + the person/project description (timeline.go); rune-truncation otherwise preserves escape bytes and the alt-screen executes OSC sequences.
  • Presenter: strip in formatText/formatPerson/formatPeople and the rendered affordance commands (presenter/format.go, presenter/render.go).

Tests

TestWithSummaryStripsANSI locks in source-level stripping.


Part 2/6 of the stacked security series. Base: security/01-api-token-leak.

📚 Stack (merge bottom-up)

  1. security: reject foreign-host URLs in api to prevent token leak #478 — reject foreign-host URLs in api (base main)
  2. security: strip ANSI/OSC escapes from API-controlled output #479 — strip ANSI/OSC escapes from output
  3. security: harden OAuth discovery and token endpoints #480 — harden OAuth discovery / token endpoints
  4. security: close config trust-boundary gaps and gate completion loader #481 — close config trust-boundary gaps + completion gate
  5. security: tighten config-dir perms and validate plugin scope argv #482 — tighten config-dir perms + plugin scope argv
  6. security: pin the gitleaks download with a SHA-256 checksum #483 — bump toolchain/x-net + CI gates

Each is independent except #482 depends on #481 (shared root.go). #478 can land first/alone.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 6 files

Re-trigger cubic

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR mitigates terminal-injection risk by stripping ANSI/OSC escape sequences from API-controlled output before it reaches styled, markdown, presenter, and watch-mode render paths.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Changes:

  • Adds source-level sanitization for response summaries, notices, and diagnostics.
  • Adds sink-level stripping in styled/markdown renderers and presenter affordance formatting.
  • Sanitizes timeline watch TUI fields before rendering/truncation and adds coverage for response option sanitization.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/output/envelope.go Strips ANSI/OSC escapes in response options and entity presenter summary/notice output.
internal/output/render.go Applies defense-in-depth stripping in styled and markdown response renderers.
internal/output/output_test.go Adds tests for summary/notice/diagnostic source-level sanitization.
internal/presenter/format.go Sanitizes formatted API-controlled text and person names.
internal/presenter/render.go Sanitizes rendered affordance commands after template interpolation.
internal/commands/timeline.go Sanitizes watch TUI event fields and project/person descriptions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/commands/timeline.go Outdated
@jeremy jeremy force-pushed the security/01-api-token-leak branch from b6d427d to 2eacce7 Compare May 30, 2026 06:05
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 7b1d3ea to 61aea03 Compare May 30, 2026 06:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61aea0335f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/presenter/render.go Outdated
Copilot AI review requested due to automatic review settings May 30, 2026 06:13
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 61aea03 to e06c5c2 Compare May 30, 2026 06:13
@github-actions github-actions Bot added the bug Something isn't working label May 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread internal/presenter/format.go Outdated
Comment thread internal/presenter/format.go Outdated
@jeremy jeremy force-pushed the security/01-api-token-leak branch from 2eacce7 to 447cbf3 Compare May 30, 2026 06:23
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from e06c5c2 to 9103d13 Compare May 30, 2026 06:23

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9103d138ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/presenter/format.go Outdated
@jeremy jeremy force-pushed the security/01-api-token-leak branch from 447cbf3 to 2734359 Compare May 30, 2026 07:51
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 9103d13 to dd63a13 Compare May 30, 2026 07:51

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd63a1322a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/presenter/format.go Outdated
Copilot AI review requested due to automatic review settings May 30, 2026 08:27
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from dd63a13 to 4566262 Compare May 30, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread internal/presenter/render.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

@jeremy jeremy force-pushed the security/01-api-token-leak branch from dfb4c74 to fc7bafa Compare July 13, 2026 23:48
Copilot AI review requested due to automatic review settings July 13, 2026 23:48
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from e4b743e to 60dcdca Compare July 13, 2026 23:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60dcdca80d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/output/envelope.go Outdated
Copilot AI review requested due to automatic review settings July 14, 2026 00:07
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 60dcdca to 8c19e62 Compare July 14, 2026 00:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread internal/presenter/format.go Outdated
Comment thread internal/presenter/format.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c19e62100

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/presenter/format.go Outdated
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 8c19e62 to e1e0036 Compare July 14, 2026 00:28
Copilot AI review requested due to automatic review settings July 14, 2026 00:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 14, 2026 01:06
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from e1e0036 to 76fcf9e Compare July 14, 2026 01:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76fcf9e384

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/output/envelope.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread internal/output/render.go
Comment on lines +116 to 127
// Summary line. SanitizeTerminal guards against terminal injection from
// API-controlled summary/notice content (defense-in-depth: also sanitized
// at the WithSummary/WithNotice source).
if resp.Summary != "" {
b.WriteString(r.Summary.Render(resp.Summary))
b.WriteString(r.Summary.Render(richtext.SanitizeTerminal(resp.Summary)))
b.WriteString("\n")
}

// Notice (e.g., truncation warning)
if resp.Notice != "" {
b.WriteString(r.Hint.Render(resp.Notice))
b.WriteString(r.Hint.Render(richtext.SanitizeTerminal(resp.Notice)))
b.WriteString("\n")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagged for human review — this asks to convert renderData/formatCell/formatDetailValue's ansi.Strip calls, but those lines are pre-existing on main and outside this PR's diff. They do share the C1 gap; deferring to a follow-up PR that sweeps all ~20 pre-existing bare ansi.Strip sites (output/render.go, tui views) to richtext.SanitizeTerminal rather than expanding this PR's scope.

API-controlled strings (names, summaries, notices, headlines, affordance
commands) reached styled/markdown sinks and the --watch TUI without ANSI
stripping, allowing terminal/OSC injection. Sanitize WithSummary/WithNotice/
WithDiagnostic at the source, the timeline watch TUI fields (creator name
stripped before the empty check so an all-escape name still falls back to a
placeholder), and presenter format helpers, with defense-in-depth at the
render sinks. Stripping is centralized in RenderTemplate + RenderHeadline so
schema headlines ({{.name}}/{{.subject}}/{{.content}}) and affordance commands
are all covered, including the identity-label headline fallback.
Copilot AI review requested due to automatic review settings July 14, 2026 01:35
@jeremy jeremy force-pushed the security/02-output-ansi-sanitize branch from 76fcf9e to 1e3cb4c Compare July 14, 2026 01:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment on lines 299 to +303
if s, ok := result.(string); ok {
fmt.Fprintln(w.opts.Writer, s)
fmt.Fprintln(w.opts.Writer, richtext.SanitizeTerminal(s))
} else {
raw, err := json.Marshal(result)
// Compound results (arrays/objects) are marshaled, but Go's JSON
// encoder only escapes C0 controls — UTF-8-encoded C1 controls

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e3cb4c6c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/output/render.go
// at the WithSummary/WithNotice source).
if resp.Summary != "" {
b.WriteString(r.Summary.Render(resp.Summary))
b.WriteString(r.Summary.Render(richtext.SanitizeTerminal(resp.Summary)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sanitize generic response data too

This only sanitizes the summary/notice strings, but the same RenderResponse path still renders resp.Data through renderData/formatCell/formatDetailValue, which still use ansi.Strip and do not strip after HTMLToMarkdown. For generic outputs such as basecamp api get ... --styled or any response without an entity schema, an API field like <p>&#x1b;[31mpwn</p> is decoded by HTMLToMarkdown and then emitted through lipgloss.Render with a live ESC sequence; apply richtext.SanitizeTerminal to the generic data formatters as well.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working commands CLI command implementations output Output formatting and presentation tests Tests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants